package net.alche.sdd.comm;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.annotation.Annotation;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import net.alche.al.util.AC;
import net.alche.al.util.M;
import net.alche.al.util.alches.classtool.Scanner;

@SuppressWarnings("all")
public class SddClassLeader extends ClassLoader {
  public SddClassLeader leaderPre = null;
  String root = "";
  String fxName = "fx";
  public Map<String, Object> beans = null;
  Map<String, String[]> beansLinkMp = new HashMap<>();
  ClassLoader parent = null;
  Map<String, Class> hasMap = new HashMap<>();

  public SddClassLeader(String root) {
    parent = ClassLoader.getSystemClassLoader();
    this.root = root;
  }

  public SddClassLeader(SddClassLeader lper) {
    parent = ClassLoader.getSystemClassLoader();
    leaderPre = lper;
    beansLinkMp = lper.beansLinkMp;
    this.root = lper.getRoot();
  }

  public SddClassLeader(String root, ClassLoader parent) {
    super(parent);
    this.root = root;
  }

  public void initImports() {
    int count = 0;
    Set<Map.Entry<String, Object>> sets = beans.entrySet();
    for (Map.Entry<String, Object> en : sets) {
      Object bean = en.getValue();
      String[] arr = (String[]) M.readField(bean, "fa_links");
      beansLinkMp.put(en.getValue().getClass().getPackageName() + "." + en.getKey(), arr);
      count++;
    }
  }

  // 检查影响
  public String[] getHotEffect(String[] names) {
    Map<String, Boolean> mp = new HashMap();
    for (String key : names) {
      // key = key.substring(fxName.length() + 1);
      checkHotEffectOne(mp, key);
    }
    String[] effects = new String[mp.size()];
    int i = 0;
    for (String key : mp.keySet()) {
      effects[i++] = key;
    }
    return effects;
  }

  public void checkHotEffectOne(Map<String, Boolean> mp, String name) {
    for (Map.Entry<String, String[]> en : beansLinkMp.entrySet()) {
      if (mp.containsKey(name)) {
        return;
      }
      for (String ss : en.getValue()) {
        if (ss.equals(name)) {
          mp.put(en.getKey(), true);
          checkHotEffectOne(mp, en.getKey());
          break;
        }
      }
    }
  }

  public String getRoot() {
    return root;
  }

  public Class getCls(String name) {
    Class cls = hasMap.get(name);
    if (cls == null) {
      try {
        cls = loadClass(name);
      } catch (Exception ex) {
        ex.printStackTrace();
      }
    }
    return cls;
  }

  // public <A extends Annotation> Set<Class<?>> getClassList(Class<?> anno) {
  public <A extends Annotation> Set<Class<?>> getClassList(String annName) {
    Set<Class<?>> controllers = new HashSet<>();
    Class anno = getCls(annName);
    Set<String> sets = hasMap.keySet();
    // for (String key : sets) {
    // Class cls = hasMap.get(key);
    for (Class cls : hasMap.values()) {
      if (cls.getAnnotation(anno) != null) {
        controllers.add(cls);
      }
    }
    return controllers;
  }

  public void initAll() {
    File[] fs = AC.file.ls(root + "fx");
    initAllRecur(fs, fxName);
  }

  public void initLs(String[] names) {
    for (String str : names) {
      loadClass(str);
    }
  }

  public void initAllRecur(File[] fs, String parName) {
    if (fs != null && fs.length > 0) {
      for (File f : fs) {
        if (f.isDirectory()) {
          initAllRecur(f.listFiles(), parName + "." + f.getName());
        } else if (!f.getName().contains("$")) {
          String packName = parName + "." + f.getName().substring(0, f.getName().length() - 6);
          loadClass(packName);
        }
      }
    }
  }

  public Object getNewObj(String packName) {
    try {
      Class cls = loadClass(packName);
      return cls.getDeclaredConstructor().newInstance();
    } catch (Exception ex) {
      ex.printStackTrace();
    }
    return null;
  }

  public Class loadClass(String packName) {
    if (hasMap.get(packName) != null) {
      return hasMap.get(packName);
    }
    if (!packName.startsWith(fxName + ".")) {
      try {
        return super.loadClass(packName, false);
      } catch (ClassNotFoundException e) {
        e.printStackTrace();
        return null;
      }
    }
    Class cls = null;
    String fileRelPath = M.replaceAll(packName, "\\.", "\\/");
    String full = root + fileRelPath + ".class";
    File classF = new File(full);
    try {
      cls = instantiateClass(packName, new FileInputStream(classF), classF.length());
      hasMap.put(packName, cls);
    } catch (Exception e) {
      e.printStackTrace();
    }
    return cls;
  }

  protected Class<?> loadClass(String name, boolean resolve)
      throws ClassNotFoundException {
    synchronized (getClassLoadingLock(name)) {
      // 先查询类是否已经被加载
      Class<?> c = hasMap.get(name);
      if (c == null) {
        c = findLoadedClass(name);
      }
      if (c == null) {
        c = loadClass(name);
        if (c == null) {
          long t1 = System.nanoTime();
          c = findClass(name);
        }
      }
      if (resolve) {
        resolveClass(c);
      }
      return c;
    }
  }

  private Class instantiateClass(String name, InputStream fin, long len) throws IOException {
    byte[] raw = new byte[(int) len];
    fin.read(raw);
    fin.close();
    return defineClass(name, raw, 0, raw.length);
  }

}
